home *** CD-ROM | disk | FTP | other *** search
- <?php
- /*
- * License Information:
- *
- * Net_DNS: A resolver library for PHP
- * Copyright (C) 2002 Eric Kilfoil eric@ypass.net
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /* Net_DNS_Packet object definition {{{ */
- /**
- * A object represation of a DNS packet (RFC1035)
- *
- * This object is used to manage a DNS packet. It contains methods for
- * DNS packet compression as defined in RFC1035, as well as parsing a DNS
- * packet response from a DNS server, or building a DNS packet from the
- * instance variables contained in the class.
- *
- * @package Net_DNS
- */
- class Net_DNS_Packet
- {
- /* class variable definitions {{{ */
- /**
- * debugging flag
- *
- * If set to TRUE (non-zero), debugging code will be displayed as the
- * packet is parsed.
- *
- * @var boolean $debug
- * @access public
- */
- var $debug;
- /**
- * A packet Header object.
- *
- * An object of type Net_DNS_Header which contains the header
- * information of the packet.
- *
- * @var object Net_DNS_Header $header
- * @access public
- */
- var $header;
- /**
- * A hash of compressed labels
- *
- * A list of all labels which have been compressed in the DNS packet
- * and the location offset of the label within the packet.
- *
- * @var array $compnames
- */
- var $compnames;
- /**
- * The origin of the packet, if the packet is a server response.
- *
- * This contains a string containing the IP address of the name server
- * from which the answer was given.
- *
- * @var string $answerfrom
- * @access public
- */
- var $answerfrom;
- /**
- * The size of the answer packet, if the packet is a server response.
- *
- * This contains a integer containing the size of the DNS packet the
- * server responded with if this packet was received by a DNS server
- * using the query() method.
- *
- * @var string $answersize
- * @access public
- */
- var $answersize;
- /**
- * An array of Net_DNS_Question objects
- *
- * Contains all of the questions within the packet. Each question is
- * stored as an object of type Net_DNS_Question.
- *
- * @var array $question
- * @access public
- */
- var $question;
-
- /* }}} */
- /* class constructor - Net_DNS_Packet($debug = FALSE) {{{ */
- /*
- * unfortunately (or fortunately), we can't follow the same
- * silly method for determining if name is a hostname or a packet
- * stream in PHP, since there is no ref() function. So we're going
- * to define a new method called parse to deal with this
- * circumstance and another method called buildQuestion to build a question.
- * I like it better that way anyway.
- */
- /**
- * Initalizes a Net_DNS_Packet object
- *
- * @param boolean $debug Turns debugging on or off
- */
- function Net_DNS_Packet($debug = FALSE)
- {
- $this->debug = $debug;
- $this->compnames = array();
- }
-
- /* }}} */
- /* Net_DNS_Packet::buildQuestion($name, $type = "A", $class = "IN") {{{ */
- /**
- * Adds a DNS question to the DNS packet
- *
- * @param string $name The name of the record to query
- * @param string $type The type of record to query
- * @param string $class The class of record to query
- * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
- */
- function buildQuestion($name, $type = "A", $class = "IN")
- {
- $this->header = new Net_DNS_Header();
- $this->header->qdcount = 1;
- $this->question[0] = new Net_DNS_Question($name, $type, $class);
- $this->answer = NULL;
- $this->authority = NULL;
- $this->additional = NULL;
- if ($this->debug) {
- $this->display();
- }
- }
-
- /* }}} */
- /* Net_DNS_Packet::parse($data) {{{ */
- /**
- * Parses a DNS packet returned by a DNS server
- *
- * Parses a complete DNS packet and builds an object hierarchy
- * containing all of the parts of the packet:
- * <ul>
- * <li>HEADER
- * <li>QUESTION
- * <li>ANSWER || PREREQUISITE
- * <li>ADDITIONAL || UPDATE
- * <li>AUTHORITY
- * </ul>
- *
- * @param string $data A binary string containing a DNS packet
- * @return boolean TRUE on success, NULL on parser error
- */
- function parse($data)
- {
- if ($this->debug) {
- echo ";; HEADER SECTION\n";
- }
-
- $this->header = new Net_DNS_Header($data);
-
- if ($this->debug) {
- $this->header->display();
- }
-
- /*
- * Print and parse the QUESTION section of the packet
- */
- if ($this->debug) {
- echo "\n";
- $section = ($this->header->opcode == "UPDATE") ? "ZONE" : "QUESTION";
- echo ";; $section SECTION (" . $this->header->qdcount . " record" .
- ($this->header->qdcount == 1 ? "" : "s") . ")\n";
- }
-
- $offset = 12;
-
- $this->question = array();
- for ($ctr = 0; $ctr < $this->header->qdcount; $ctr++) {
- list($qobj, $offset) = $this->parse_question($data, $offset);
- if (is_null($qobj)) {
- return(NULL);
- }
-
- $this->question[count($this->question)] = $qobj;
- if ($this->debug) {
- echo ";;\n;";
- $qobj->display();
- }
- }
-
- /*
- * Print and parse the PREREQUISITE or ANSWER section of the packet
- */
- if ($this->debug) {
- echo "\n";
- $section = ($this->header->opcode == "UPDATE") ? "PREREQUISITE" :"ANSWER";
- echo ";; $section SECTION (" .
- $this->header->ancount . " record" .
- (($this->header->ancount == 1) ? "" : "s") .
- ")\n";
- }
-
- $this->answer = array();
- for ($ctr = 0; $ctr < $this->header->ancount; $ctr++) {
- list($rrobj, $offset) = $this->parse_rr($data, $offset);
-
- if (is_null($rrobj)) {
- return(NULL);
- }
- array_push($this->answer, $rrobj);
- if ($this->debug) {
- $rrobj->display();
- }
- }
-
- /*
- * Print and parse the UPDATE or AUTHORITY section of the packet
- */
- if ($this->debug) {
- echo "\n";
- $section = ($this->header->opcode == "UPDATE") ? "UPDATE" : "AUTHORITY";
- echo ";; $section SECTION (" .
- $this->header->nscount . " record" .
- (($this->header->nscount == 1) ? "" : "s") .
- ")\n";
- }
-
- $this->authority = array();
- for ($ctr = 0; $ctr < $this->header->nscount; $ctr++) {
- list($rrobj, $offset) = $this->parse_rr($data, $offset);
-
- if (is_null($rrobj)) {
- return(NULL);
- }
- array_push($this->authority, $rrobj);
- if ($this->debug) {
- $rrobj->display();
- }
- }
-
- /*
- * Print and parse the ADDITIONAL section of the packet
- */
- if ($this->debug) {
- echo "\n";
- echo ";; ADDITIONAL SECTION (" .
- $this->header->arcount . " record" .
- (($this->header->arcount == 1) ? "" : "s") .
- ")\n";
- }
-
- $this->additional = array();
- for ($ctr = 0; $ctr < $this->header->arcount; $ctr++) {
- list($rrobj, $offset) = $this->parse_rr($data, $offset);
-
- if (is_null($rrobj)) {
- return(NULL);
- }
- array_push($this->additional, $rrobj);
- if ($this->debug) {
- $rrobj->display();
- }
- }
-
- return(TRUE);
- }
-
- /* }}} */
- /* Net_DNS_Packet::data() {{{*/
- /**
- * Build a packet from a Packet object hierarchy
- *
- * Builds a valid DNS packet suitable for sending to a DNS server or
- * resolver client containing all of the data in the packet hierarchy.
- *
- * @return string A binary string containing a DNS Packet
- */
- function data()
- {
- $data = $this->header->data();
-
- for ($ctr = 0; $ctr < $this->header->qdcount; $ctr++) {
- $data .= $this->question[$ctr]->data($this, strlen($data));
- }
-
- for ($ctr = 0; $ctr < $this->header->ancount; $ctr++) {
- $data .= $this->answer[$ctr]->data($this, strlen($data));
- }
-
- for ($ctr = 0; $ctr < $this->header->nscount; $ctr++) {
- $data .= $this->authority[$ctr]->data($this, strlen($data));
- }
-
- for ($ctr = 0; $ctr < $this->header->arcount; $ctr++) {
- $data .= $this->additional[$ctr]->data($this, strlen($data));
- }
-
- return($data);
- }
-
- /*}}}*/
- /* Net_DNS_Packet::dn_comp($name, $offset) {{{*/
- /**
- * DNS packet compression method
- *
- * Returns a domain name compressed for a particular packet object, to
- * be stored beginning at the given offset within the packet data. The
- * name will be added to a running list of compressed domain names for
- * future use.
- *
- * @param string $name The name of the label to compress
- * @param integer $offset The location offset in the packet to where
- * the label will be stored.
- * @return string $compname A binary string containing the compressed
- * label.
- * @see Net_DNS_Packet::dn_expand()
- */
- function dn_comp($name, $offset)
- {
- $names = explode(".", $name);
- $compname = "";
- while (count($names)) {
- $dname = join(".", $names);
- if (isset($this->compnames[$dname])) {
- $compname .= pack("n", 0xc000 | $this->compnames[$dname]);
- break;
- }
-
- $this->compnames[$dname] = $offset;
- $first = array_shift($names);
- $length = strlen($first);
- $compname .= pack("Ca*", $length, $first);
- $offset += $length + 1;
- }
- if (! count($names)) {
- $compname .= pack("C", 0);
- }
- return($compname);
- }
-
- /*}}}*/
- /* Net_DNS_Packet::dn_expand($packet, $offset) {{{ */
- /**
- * DNS packet decompression method
- *
- * Expands the domain name stored at a particular location in a DNS
- * packet. The first argument is a variable containing the packet
- * data. The second argument is the offset within the packet where
- * the (possibly) compressed domain name is stored.
- *
- * @param string $packet The packet data
- * @param integer $offset The location offset in the packet of the
- * label to decompress.
- * @return array Returns a list of type array($name, $offset) where
- * $name is the name of the label which was decompressed
- * and $offset is the offset of the next field in the
- * packet. Returns array(NULL, NULL) on error
- */
- function dn_expand($packet, $offset)
- {
- $packetlen = strlen($packet);
- $int16sz = 2;
- $name = "";
- while (1) {
- if ($packetlen < ($offset + 1)) {
- return(array(NULL, NULL));
- }
-
- $a = unpack("@$offset/Cchar", $packet);
- $len = $a["char"];
-
- if ($len == 0) {
- $offset++;
- break;
- } else if (($len & 0xc0) == 0xc0) {
- if ($packetlen < ($offset + $int16sz)) {
- return(array(NULL, NULL));
- }
- $ptr = unpack("@$offset/ni", $packet);
- $ptr = $ptr["i"];
- $ptr = $ptr & 0x3fff;
- $name2 = Net_DNS_Packet::dn_expand($packet, $ptr);
-
- if (is_null($name2[0])) {
- return(array(NULL, NULL));
- }
- $name .= $name2[0];
- $offset += $int16sz;
- break;
- } else {
- $offset++;
-
- if ($packetlen < ($offset + $len)) {
- return(array(NULL, NULL));
- }
-
- $elem = substr($packet, $offset, $len);
- $name .= $elem . ".";
- $offset += $len;
- }
- }
- $name = ereg_replace("\.$", "", $name);
- return(array($name, $offset));
- }
-
- /*}}}*/
- /* Net_DNS_Packet::parse_question($data, $offset) {{{ */
- /**
- * Parses the question section of a packet
- *
- * Examines a DNS packet at the specified offset and parses the data
- * of the QUESTION section.
- *
- * @param string $data The packet data returned from the server
- * @param integer $offset The location offset of the start of the
- * question section.
- * @return array An array of type array($q, $offset) where $q
- * is a Net_DNS_Question object and $offset is the
- * location of the next section of the packet which
- * needs to be parsed.
- */
- function parse_question($data, $offset)
- {
- list($qname, $offset) = $this->dn_expand($data, $offset);
- if (is_null($qname)) {
- return(array(NULL, NULL));
- }
-
- if (strlen($data) < ($offset + 2 * 2)) {
- return(array(NULL, NULL));
- }
-
- $q = unpack("@$offset/n2int", $data);
- $qtype = $q["int1"];
- $qclass = $q["int2"];
- $offset += 2 * 2;
-
- $qtype = Net_DNS::typesbyval($qtype);
- $qclass = Net_DNS::classesbyval($qclass);
-
- $q = new Net_DNS_Question($qname, $qtype, $qclass);
- return(array($q, $offset));
- }
-
- /*}}}*/
- /* Net_DNS_Packet::parse_rr($data, $offset) {{{ */
- /**
- * Parses a resource record section of a packet
- *
- * Examines a DNS packet at the specified offset and parses the data
- * of a section which contains RRs (ANSWER, AUTHORITY, ADDITIONAL).
- *
- * @param string $data The packet data returned from the server
- * @param integer $offset The location offset of the start of the resource
- * record section.
- * @return array An array of type array($rr, $offset) where $rr
- * is a Net_DNS_RR object and $offset is the
- * location of the next section of the packet which
- * needs to be parsed.
- */
- function parse_rr($data, $offset)
- {
- list($name, $offset) = $this->dn_expand($data, $offset);
- if (! strlen($name)) {
- return(array(NULL, NULL));
- }
-
- if (strlen($data) < ($offset + 10)) {
- return(array(NULL, NULL));
- }
-
- $a = unpack("@$offset/n2tc/Nttl/nrdlength", $data);
- $type = $a["tc1"];
- $class = $a["tc2"];
- $ttl = $a["ttl"];
- $rdlength = $a["rdlength"];
-
- $type = Net_DNS::typesbyval($type);
- $class = Net_DNS::classesbyval($class);
-
- $offset += 10;
- if (strlen($data) < ($offset + $rdlength)) {
- return(array(NULL, NULL));
- }
-
- $rrobj = new Net_DNS_RR(array($name,
- $type,
- $class,
- $ttl,
- $rdlength,
- $data,
- $offset));
-
- if (is_null($rrobj)) {
- return(array(NULL, NULL));
- }
-
- $offset += $rdlength;
-
- return(array($rrobj, $offset));
- }
-
- /* }}} */
- /* Net_DNS_Packet::display() {{{ */
- /**
- * Prints out the packet in a human readable formatted string
- */
- function display()
- {
- echo $this->string();
- }
-
- /*}}}*/
- /* Net_DNS_Packet::string() {{{ */
- /**
- * Builds a human readable formatted string representing a packet
- */
- function string()
- {
- $retval = "";
- if ($this->answerfrom) {
- $retval .= ";; Answer received from " . $this->answerfrom . "(" .
- $this->answersize . " bytes)\n;;\n";
- }
-
- $retval .= ";; HEADER SECTION\n";
- $retval .= $this->header->string();
- $retval .= "\n";
-
- $section = ($this->header->opcode == "UPDATE") ? "ZONE" : "QUESTION";
- $retval .= ";; $section SECTION (" . $this->header->qdcount .
- " record" . ($this->header->qdcount == 1 ? "" : "s") .
- ")\n";
-
- foreach ($this->question as $qr) {
- $retval .= ";; " . $qr->string() . "\n";
- }
-
- $section = ($this->header->opcode == "UPDATE") ? "PREREQUISITE" : "ANSWER";
- $retval .= "\n;; $section SECTION (" . $this->header->ancount .
- " record" . ($this->header->ancount == 1 ? "" : "s") .
- ")\n";
-
- if (is_array($this->answer)) {
- foreach ($this->answer as $ans) {
- $retval .= ";; " . $ans->string() . "\n";
- }
- }
-
- $section = ($this->header->opcode == "UPDATE") ? "UPDATE" : "AUTHORITY";
- $retval .= "\n;; $section SECTION (" . $this->header->nscount .
- " record" . ($this->header->nscount == 1 ? "" : "s") .
- ")\n";
-
- if (is_array($this->authority)) {
- foreach ($this->authority as $auth) {
- $retval .= ";; " . $auth->string() . "\n";
- }
- }
-
- $retval .= "\n;; ADDITIONAL SECTION (" . $this->header->arcount .
- " record" . ($this->header->arcount == 1 ? "" : "s") .
- ")\n";
-
- if (is_array($this->additional)) {
- foreach ($this->additional as $addl) {
- $retval .= ";; " . $addl->string() . "\n";
- }
- }
-
- $retval .= "\n\n";
- return($retval);
- }
-
- /*}}}*/
- }
- /* }}} */
- /* VIM settings {{{
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * soft-stop-width: 4
- * c indent on
- * End:
- * vim600: sw=4 ts=4 sts=4 cindent fdm=marker et
- * vim<600: sw=4 ts=4
- * }}} */
- ?>
-